package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.dto.BillDto;
import com.zzyl.dto.CheckInApplyDto;
import com.zzyl.dto.CheckInConfigDto;
import com.zzyl.entity.*;
import com.zzyl.enums.*;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.*;
import com.zzyl.service.BillService;
import com.zzyl.service.CheckInService;
import com.zzyl.service.ElderService;
import com.zzyl.service.NursingTaskService;
import com.zzyl.utils.CodeUtil;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.*;
import com.zzyl.vo.retreat.ElderVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 入住管理
 *
 * @author itcast
 * @create 2023/12/18 18:40
 **/
@Service
public class CheckInServiceImpl implements CheckInService {
    @Autowired
    private ElderService elderService;
    @Autowired
    private CheckInMapper checkInMapper;
    @Autowired
    private BedMapper bedMapper;
    @Autowired
    private CheckInConfigMapper checkInConfigMapper;
    @Autowired
    private BalanceMapper balanceMapper;
    @Autowired
    private BillService billService;
    @Autowired
    private NursingTaskService nursingTaskService;
    @Autowired
    private ContractMapper contractMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 入住编号前缀
     */
    private static final String CHECK_IN_CODE_PREFIX = "RZ";

    /**
     * 合同编号前缀
     */
    private static final String CONTRACT_CODE_PREFIX = "HT";

    /**
     * 申请入住
     *
     * @param checkInApplyDto 申请入住请求模型
     */
    @Override
    public void apply(CheckInApplyDto checkInApplyDto) {
        //1.校验老人是否已经入住
        ElderVo elderVo = elderService.selectByIdCardAndStatus(checkInApplyDto.getCheckInElderDto().getIdCardNo(), ElderStatusEnum.CHECK_IN.getOrdinal());
        if (ObjectUtil.isNotEmpty(elderVo)) {
            throw new BaseException(BasicEnum.ELDER_ALREADY_CHECKIN);
        }

        //2.校验费用时间是否在入住时间内
        CheckInConfigDto checkInConfigDto = checkInApplyDto.getCheckInConfigDto();
        if (checkInConfigDto.getCheckInStartTime().isAfter(checkInConfigDto.getCostStartTime())
                || checkInConfigDto.getCheckInEndTime().isBefore(checkInConfigDto.getCostEndTime())) {
            throw new BaseException(BasicEnum.CHECKIN_TERM_SHOULD_CONTAIN_COST_TERM);
        }

        //3.更新床位状态
        bedMapper.updateBedStatusById(checkInConfigDto.getBedId(), BedStatusEnum.OCCUPIED.getOrdinal());

        //4.新增或更新老人，老人可能以前入住过。如果数据库存在老人信息就是更新，不存在就是新增
        Bed bed = bedMapper.getBedById(checkInConfigDto.getBedId());
        Elder elder = insertOrUpdateElder(checkInApplyDto, bed);

        //生成入住编码
        String checkInCode = CodeUtil.generateCode(CHECK_IN_CODE_PREFIX, redisTemplate, 5);
        //生成合同编码
        String contractNo = CodeUtil.generateCode(CONTRACT_CODE_PREFIX, redisTemplate, 5);

        //5.新增合同
        insertConTract(checkInApplyDto, elder, checkInCode, contractNo);

        //6.新增入住信息
        insertCheckIn(checkInApplyDto, elder, checkInCode, contractNo);

        //7.新增入住配置
        CheckInConfig checkInConfig = insertCheckInConfig(checkInConfigDto, bed.getBedNumber(), elder.getId(), checkInCode);

        //8.新增或更新余额信息
        insertOrUpdateBalance(elder, checkInConfig);

        //9.新增首月账单
        insertFirstMonthBill(elder.getId(), checkInConfig.getCostStartTime(), checkInCode);

        //10.生成护理任务
        nursingTaskService.createMonthTask(BeanUtil.toBean(elder, ElderVo.class), checkInApplyDto.getCheckInContractDto().getSignDate(), null);
    }

    /**
     * 新增首月账单
     *
     * @param elderId       老人id
     * @param costStartTime 费用开始时间
     * @param checkInCode   入住编码
     */
    private void insertFirstMonthBill(Long elderId, LocalDateTime costStartTime, String checkInCode) {
        //生成首月账单
        BillDto billDto = new BillDto();
        String dateStr = LocalDateTimeUtil.format(costStartTime, "yyyy-MM");
        billDto.setBillMonth(dateStr);
        billDto.setElderId(elderId);
        billDto.setCheckInCode(checkInCode);
        billService.createMonthBill(billDto);
    }

    /**
     * 新增或更新余额信息
     *
     * @param elder         老人信息
     * @param checkInConfig 入住申请
     */
    private void insertOrUpdateBalance(Elder elder, CheckInConfig checkInConfig) {
        Balance balance = new Balance();
        balance.setElderId(elder.getId());
        balance.setElderName(elder.getName());
        balance.setBedNo(elder.getBedNumber());
        balance.setDepositAmount(checkInConfig.getDepositAmount());
        balance.setPrepaidBalance(new BigDecimal(0));
        balance.setArrearsAmount(new BigDecimal(0));
        balance.setStatus(BalanceStatusEnum.NORMAL.getOrdinal());

        Balance dbBalance = balanceMapper.selectByElderId(elder.getId());
        if (ObjectUtil.isNotEmpty(dbBalance)) {
            balance.setId(dbBalance.getId());
            balanceMapper.updateByPrimaryKeySelective(balance);
        } else {
            balanceMapper.insert(balance);
        }
    }

    /**
     * 新增入住信息
     *
     * @param checkInApplyDto 入住申请信息
     * @param elder           老人信息
     * @param checkInCode     入住编码
     * @param contractNo      合同编码
     */
    private void insertCheckIn(CheckInApplyDto checkInApplyDto, Elder elder, String checkInCode, String contractNo) {
        //获取当前登录人的信息
        String subject = UserThreadLocal.getSubject();
        User user = JSONUtil.toBean(subject, User.class);

        CheckIn checkIn = new CheckIn();
        checkIn.setCheckInCode(checkInCode);
        checkIn.setTitle(elder.getName() + "的入住申请");
        checkIn.setElderId(elder.getId());
        checkIn.setCheckInTime(LocalDateTime.now());
        checkIn.setRemark(contractNo);
        checkIn.setApplicat(user.getRealName());
        checkIn.setDeptNo(user.getDeptNo());
        checkIn.setApplicatId(user.getId());
        checkIn.setStatus(CheckInStatusEnum.PROGRESSING.getOrdinal());
        checkIn.setOtherApplyInfo(JSONUtil.toJsonStr(checkInApplyDto.getElderFamilyDtoList()));
        checkInMapper.insert(checkIn);
    }

    /**
     * 新增合同
     *
     * @param checkInApplyDto 入住申请信息
     * @param elder           老人信息
     * @param checkInCode     入住编码
     * @param contractNo      合同编码
     */
    private void insertConTract(CheckInApplyDto checkInApplyDto, Elder elder, String checkInCode, String contractNo) {
        //如果入住开始时间小于当前时间，则合同状态设为生效中，否则为待生效
        LocalDateTime checkInStartTime = checkInApplyDto.getCheckInConfigDto().getCheckInStartTime();
        Integer contractStatus = checkInStartTime.isBefore(LocalDateTime.now()) ? ContractStatusEnum.EFFECTIVE.getOrdinal() : ContractStatusEnum.PENDING_EFFECTIVE.getOrdinal();

        //新增合同
        Contract contract = BeanUtil.toBean(checkInApplyDto.getCheckInContractDto(), Contract.class);
        contract.setContractNo(contractNo);
        contract.setCheckInNo(checkInCode);
        contract.setElderId(elder.getId());
        contract.setElderName(elder.getName());
        contract.setStatus(contractStatus);
        contract.setStartTime(checkInApplyDto.getCheckInConfigDto().getCheckInStartTime());
        contract.setEndTime(checkInApplyDto.getCheckInConfigDto().getCheckInEndTime());
        contractMapper.insert(contract);
    }

    /**
     * 新增入住配置
     *
     * @param checkInConfigDto 入住申请信息
     * @param bedNumber        床位编号
     * @param elderId          老人id
     * @param checkInCode      入住编码
     * @return 入主配置
     */
    private CheckInConfig insertCheckInConfig(CheckInConfigDto checkInConfigDto, String bedNumber, Long elderId, String checkInCode) {
        CheckInConfig checkInConfig = BeanUtil.toBean(checkInConfigDto, CheckInConfig.class);
        checkInConfig.setElderId(elderId);
        checkInConfig.setBedNumber(bedNumber);
        checkInConfig.setCheckInCode(checkInCode);
        String remark = checkInConfigDto.getFloorId()
                + ":" + checkInConfigDto.getRoomId()
                + ":" + checkInConfigDto.getBedId()
                + ":" + checkInConfigDto.getFloorName()
                + ":" + checkInConfigDto.getCode();
        checkInConfig.setRemark(remark);
        checkInConfigMapper.insert(checkInConfig);
        return checkInConfig;
    }

    /**
     * 新增或更新老人信息
     *
     * @param checkInApplyDto 入住申请
     * @param bed             床位信息
     * @return 老人信息
     */
    private Elder insertOrUpdateElder(CheckInApplyDto checkInApplyDto, Bed bed) {
        Elder elder = BeanUtil.toBean(checkInApplyDto.getCheckInElderDto(), Elder.class);
        elder.setImage(checkInApplyDto.getCheckInElderDto().getOneInchPhoto());
        elder.setStatus(ElderStatusEnum.CHECK_IN.getOrdinal());
        elder.setBedId(bed.id);
        elder.setBedNumber(bed.getBedNumber());
        return elderService.insertOrUpdate(elder);
    }

    /**
     * 分页查询
     *
     * @param elderName 老人姓名，模糊查询
     * @param idCardNo  身份证号，精确查询
     * @param pageNum   页码
     * @param pageSize  页面大小
     * @return 分页结果
     */
    @Override
    public PageResponse<CheckInPageQueryVo> pageQuery(String elderName, String idCardNo, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);

        //老人姓名，模糊查询，这里进行预处理
        elderName = CharSequenceUtil.isBlank(elderName) ? null : "%" + elderName + "%";

        //这里只查询入住状态的
        Page<CheckInPageQueryVo> pageResult = checkInMapper.selectByPage(elderName, idCardNo, CheckInStatusEnum.PROGRESSING.getOrdinal());
        return PageResponse.of(pageResult, CheckInPageQueryVo.class);
    }

    /**
     * 入住详情
     *
     * @param id 入住id
     * @return 入住详情
     */
    @Override
    public CheckInDetailVo detail(Long id) {
        //入住信息
        CheckIn checkIn = checkInMapper.selectById(id);

        //老人信息
        ElderVo elderVo = elderService.selectByPrimaryKey(checkIn.getElderId());
        CheckInElderVo checkInElderVo = BeanUtil.toBean(elderVo, CheckInElderVo.class);
        checkInElderVo.setOneInchPhoto(elderVo.getImage());

        //入住配置信息
        CheckInConfig checkInConfig = checkInConfigMapper.findCurrentConfigByElderId(checkIn.getElderId());

        //合同信息
        Contract contract = contractMapper.selectByContractNo(checkIn.getRemark());

        //封装响应信息
        CheckInDetailVo checkInDetailVo = new CheckInDetailVo();
        checkInDetailVo.setCheckInElderVo(checkInElderVo);
        checkInDetailVo.setElderFamilyVoList(JSONUtil.toList(checkIn.getOtherApplyInfo(), ElderFamilyVo.class));
        checkInDetailVo.setCheckInConfigVo(BeanUtil.toBean(checkInConfig, CheckInConfigVo.class));
        checkInDetailVo.setCheckInContractVo(BeanUtil.toBean(contract, CheckInContractVo.class));
        return checkInDetailVo;
    }
}
